package com.nstskj.study.netty.tcp.netty.channel.handler;

import com.nstskj.study.netty.tcp.netty.constants.NettyProtocolConstants;
import com.nstskj.study.netty.tcp.netty.manage.LocalMananger;
import com.nstskj.study.netty.tcp.netty.manage.spring.LocalSpringBeanManager;
import com.nstskj.study.netty.tcp.netty.protocol.event.CreateNetDeviceEvent;
import com.nstskj.study.netty.tcp.netty.protocol.executor.msg.ProtocolHandlerWorkExecutor;
import com.nstskj.study.netty.tcp.netty.protocol.message.in.msg.base.AbstractInputNetTcpMessage;
import com.nstskj.study.netty.tcp.netty.protocol.session.NetDeviceSession;
import com.nstskj.study.netty.tcp.netty.protocol.session.base.AbstractSession;
import com.nstskj.study.netty.tcp.netty.protocol.session.factory.SessionIdFactory;
import com.nstskj.study.netty.tcp.netty.protocol.session.manage.NetDeviceSessionManage;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;

/**
 * @author ZhouChuGang
 * @version 1.0
 * @project study-netty-iot
 * @date 2021/4/14 16:49
 * @Description tcp消息处理 用于解析并发下
 */
@Slf4j
public class NetMessageTcpServerHandler extends SimpleChannelInboundHandler<AbstractInputNetTcpMessage> {

    /**
     * 当该连接分配到具体的worker线程后，该回调会被调用
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        Channel channel = ctx.channel();
        //这里创建连接的session对象
        log.info("设备连接 {}", channel);
        LocalSpringBeanManager springBeanManager = LocalMananger.INSTANCE.getLocalSpringBeanManager();
        SessionIdFactory sessionIdFactory = springBeanManager.getSessionIdFactory();
        String sessionId = sessionIdFactory.createSessionId(channel);
        NetDeviceSession netDeviceSession = new NetDeviceSession(sessionId, channel);
        //创建设备创建消息
        CreateNetDeviceEvent createNetDeviceEvent = new CreateNetDeviceEvent(this, netDeviceSession);
        //发布事件
        springBeanManager.getApplicationContext().publishEvent(createNetDeviceEvent);
        //把sessionId属性绑定到 管道中
        channel.attr(NettyProtocolConstants.CHANNEL_SESSION_ID).set(sessionId);
    }

    /**
     * 收到消息时执行
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, AbstractInputNetTcpMessage msg) throws Exception {
        Channel channel = ctx.channel();
        String sessionId = channel.attr(NettyProtocolConstants.CHANNEL_SESSION_ID).get();
        ProtocolHandlerWorkExecutor protocolHandlerWorkExecutor = LocalMananger.INSTANCE.getLocalSpringBeanManager().getProtocolHandlerWorkExecutor();
        //加入当前的会话id，用于溯源
        msg.attr(NettyProtocolConstants.CHANNEL_SESSION_ID).set(sessionId);
        //提交处理消息
        protocolHandlerWorkExecutor.submit(sessionId, msg);
    }

    /**
     * 接收的自定义事件
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        log.warn("收到空闲事件调用关闭---------------------------");
        close(ctx.channel());
    }

    /**
     * 出现异常
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("出现异常调用关闭---------------------------", cause);
        close(ctx.channel());
    }

    /**
     * 对应channelRegistered，当连接关闭后，释放绑定的workder线程；
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.warn("管道关闭调用关闭---------------------------");
        close(ctx.channel());
    }

    /**
     * 关闭管道
     *
     * @param channel
     */
    private void close(Channel channel) {
        String sessionId = channel.attr(NettyProtocolConstants.CHANNEL_SESSION_ID).get();
        NetDeviceSessionManage netDeviceSessionManage = LocalMananger.INSTANCE.getLocalSpringBeanManager().getNetDeviceSessionManage();
        AbstractSession session = netDeviceSessionManage.getSession(sessionId);
        if (session != null) {
            session.close();
        }
    }

}
